home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue26 / pagectrl / PAGECTL3.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-09-02  |  11.8 KB  |  418 lines

  1. //TabSheet's Tag holds the image
  2. //Can't change Tag to alter image for tab
  3. //Change tabsheet's PageIndex, glyph disappears - resizing fixes
  4. //Tabsheet can't draw buttons in bottom or right positions
  5. //Use TColorPageCtrl.TabVisible instead of
  6. //  TTabSheet.TabVisible or TTabSheet.Visible
  7.  
  8. unit PageCtl3;
  9.  
  10. interface
  11.  
  12. uses
  13.   Windows, Messages, CommCtrl, SysUtils, Classes, Graphics, Controls,
  14.   Forms, Dialogs, ComCtrls, StdCtrls, ExtCtrls;
  15.  
  16. type
  17.   TTabVPosition = (tvpNone, tvpLeft, tvpRight);
  18.   TPageControlStyle = (pcsStandard, pcsOwnerDraw);
  19.  
  20.   TColorPageControl = class(TPageControl)
  21.   private
  22.     FCanvas       : TCanvas;
  23.     FImages       : TImageList;
  24.     FOnDrawItem   : TDrawItemEvent;
  25.     FTabButtons   : Boolean;
  26.     FStyle        : TPageControlStyle;
  27.     FTabVPosition : TTabVPosition;
  28.  
  29.     procedure AddImages;
  30.     procedure DrawItem(Index: Integer; ARect: TRect;
  31.       State: TOwnerDrawState);
  32.     function IsValidTabSheet (iTab: TTabSheet): Boolean;
  33.  
  34.     function  GetTabImage(iTab: TTabSheet): Integer;
  35.     procedure SetTabImage(iTab: TTabSheet; iImageIndex: Integer);
  36.     procedure ISetTabImage(iTabIndex, iImageIndex: Integer);
  37.  
  38.     function  GetTabImageVisible(iTab: TTabSheet): Boolean;
  39.     procedure SetTabImageVisible(iTab: TTabSheet; Value: Boolean);
  40.  
  41.     function  GetTabVisible(iTab: TTabSheet): Boolean;
  42.     procedure SetTabVisible(iTab: TTabSheet; Value: Boolean);
  43.  
  44.     procedure CMControlListChange(var Msg: TCMControlListChange);
  45.       message cm_ControlListChange;
  46.  
  47.   protected
  48.     procedure AlignControls(AControl: TControl;
  49.       var Rect: TRect); override;
  50.     procedure CreateParams(var Params: TCreateParams); override;
  51.     procedure CreateWnd; override;
  52.     procedure Notification(AComponent: TComponent;
  53.       Operation: TOperation); override;
  54.  
  55.     procedure SetImages(Value: TImageList);
  56.     procedure SetStyle(Value: TPageControlStyle);
  57.     procedure SetTabButtons(Value: Boolean);
  58.     procedure SetTabVPosition(Value: TTabVPosition);
  59.  
  60.     procedure CNDrawItem(var Msg: TWMDrawItem);
  61.       message cn_DrawItem;
  62.  
  63.   public
  64.     constructor Create(AOwner: TComponent); override;
  65.     destructor Destroy; override;
  66.     procedure DefaultDrawTab(Index: Integer; ARect: TRect;
  67.       State: TOwnerDrawState); virtual;
  68.     property Canvas: TCanvas read FCanvas;
  69.     property TabImage[Index: TTabSheet]: Integer
  70.       read GetTabImage write SetTabImage;
  71.     property TabImageVisible[Index: TTabSheet]: Boolean
  72.       read GetTabImageVisible write SetTabImageVisible;
  73.     property TabVisible[Index: TTabSheet]: Boolean
  74.       read GetTabVisible write SetTabVisible;
  75.  
  76.   published
  77.     property Images: TImageList read FImages write SetImages;
  78.     property Style: TPageControlStyle
  79.       read FStyle write SetStyle default pcsStandard;
  80.     property TabButtons: Boolean
  81.       read FTabButtons write SetTabButtons default False;
  82.     property TabVPosition: TTabVPosition
  83.       read FTabVPosition write SetTabVPosition default tvpNone;
  84.     property OnDrawItem: TDrawItemEvent
  85.       read FOnDrawItem write FOnDrawItem;
  86.   end;
  87.  
  88. procedure Register;
  89.  
  90. implementation
  91.  
  92. //Delphi 2 and C++ Builder 1 don't have some
  93. //of the necessary constants or properties
  94. {$ifdef Ver90}
  95.   {$define OldCommCtrl}
  96. {$endif}
  97. {$ifdef Ver93}
  98.   {$define OldCommCtrl}
  99. {$endif}
  100. {$ifdef OldCommCtrl}
  101. const
  102.   tcs_Right = 2;
  103.   tcs_Bottom = 2;
  104.   tcs_Vertical = $80;
  105. {$endif}
  106.  
  107. constructor TColorPageControl.Create(AOwner: TComponent);
  108. begin
  109.   inherited;
  110.   FTabButtons   := False;
  111.   FStyle        := pcsStandard;
  112.   FTabVPosition := tvpNone
  113. end;
  114.  
  115. destructor TColorPageControl.Destroy;
  116. begin
  117.   //cleanup after ourselves
  118.   if Assigned(FCanvas) then
  119.     FCanvas.Free;
  120.   inherited
  121. end;
  122.  
  123. procedure TColorPageControl.AddImages;
  124. var
  125.   Loop: integer;
  126. begin
  127.   if Images <> nil then
  128.   begin
  129.     for Loop := 0 to PageCount - 1 do
  130.       ISetTabImage(Loop, Pages[Loop].Tag);
  131.     Perform(tcm_SetImageList, 0, Longint(Images.Handle));
  132.   end
  133.   else
  134.     Perform(tcm_SetImageList, 0, 0);
  135.   if csDesigning in ComponentState then
  136.     Change;
  137. end;
  138.  
  139. procedure TColorPageControl.DrawItem(Index: Integer; ARect: TRect;
  140.   State: TOwnerDrawState);
  141. begin
  142.   if Assigned(FOnDrawItem) then
  143.     FOnDrawItem(Self, Index, ARect, State)
  144.   else
  145.     DefaultDrawTab(Index, ARect, State)
  146. end;
  147.  
  148. function TColorPageControl.IsValidTabSheet(iTab: TTabSheet): Boolean;
  149. begin
  150.   //Sanity checks
  151.   Result := Assigned(iTab) and (iTab.PageControl = Self);
  152. end;
  153.  
  154. function TColorPageControl.GetTabImage(iTab: TTabSheet): Integer;
  155. var
  156.   TCItem: TTCItem;
  157. begin
  158.   //return Tab's image's index in the imagelist
  159.   Result := -1;
  160.   if not IsValidTabSheet(iTab) then
  161.     Exit;
  162.   Result := iTab.Tag;
  163.   if Result <> -1 then
  164.   begin
  165.     TCItem.mask := tcif_Image;
  166.     if Perform(tcm_GetItem, iTab.TabIndex,
  167.                Longint(@TCItem)) <> 0 then
  168.       Result   := TCItem.iImage;
  169.     iTab.Tag := Result;
  170.   end;
  171. end;
  172.  
  173. procedure TColorPageControl.SetTabImage(iTab:TTabSheet;
  174.   iImageIndex: Integer);
  175. begin
  176.   //This checks for safety and sets the tabsheet's associated image
  177.   if IsValidTabSheet(iTab) and iTab.TabVisible then
  178.     ISetTabImage(iTab.TabIndex, iTab.Tag);
  179. end;
  180.  
  181. procedure TColorPageControl.ISetTabImage(iTabIndex, iImageIndex: Integer);
  182. var
  183.   TCItem: TTCItem;
  184. begin
  185.   //Internal function to set tab image
  186.   //without affecting Tag property
  187.   TCItem.mask := tcif_Image;
  188.   //Fill the structure
  189.   Perform(tcm_GetItem, iTabIndex, Longint(@TCItem));
  190.   //Set the new image index
  191.   TCItem.iImage := iImageIndex;
  192.   Perform(tcm_SetItem, iTabIndex, Longint(@TCItem));
  193. end;
  194.  
  195. function TColorPageControl.GetTabImageVisible(iTab: TTabSheet): Boolean;
  196. var
  197.   TCItem : TTCItem;
  198. begin
  199.   //Tab images are seen as invisible if a tab hasn't got an image
  200.   Result := False;
  201.   if IsValidTabSheet(iTab) then
  202.   begin
  203.     TCItem.mask := tcif_Image;
  204.     //Fill the structure
  205.     Perform(tcm_GetItem, iTab.TabIndex, Longint(@TCItem));
  206.     Result := TCItem.iImage <> -1
  207.   end
  208. end;
  209.  
  210. procedure TColorPageControl.SetTabImageVisible(iTab: TTabSheet;
  211.   Value: Boolean);
  212. begin
  213.   //Tab images are made visible by adding them to the tab.
  214.   if IsValidTabSheet(iTab) then
  215.     if Value then
  216.       SetTabImage(iTab, iTab.Tag)
  217.     else
  218.       if TabVisible[iTab] then
  219.         ISetTabImage(iTab.TabIndex, -1)
  220. end;
  221.  
  222. function TColorPageControl.GetTabVisible (iTab: TTabSheet): Boolean;
  223. begin
  224.   //We have to use these functions to make
  225.   //tabsheets visible and invisible as Tabsheets cannot
  226.   //be made hidden - the VCL merely removes them
  227.   //and their associated images from the tab control
  228.   Result := False;
  229.   if IsValidTabSheet(iTab) then
  230.     Result := iTab.TabVisible;
  231. end;
  232.  
  233. procedure TColorPageControl.SetTabVisible(iTab: TTabSheet;
  234.   Value: Boolean);
  235. begin
  236.   //This will make a tab visible/invisible as appropriate
  237.   //However if an invisible tab had its image removed
  238.   //This code will add it back in
  239.   if IsValidTabSheet(iTab) then
  240.     if iTab.PageControl = Self then
  241.     begin
  242.       iTab.TabVisible := Value;
  243.       if iTab.TabVisible then
  244.         SetTabImage(iTab, iTab.Tag);
  245.       Realign
  246.     end;
  247. end;
  248.  
  249. procedure TColorPageControl.CMControlListChange(
  250.   var Msg: TCMControlListChange);
  251. begin
  252.   //Update the images if a new Tabsheet is installed
  253.   with Msg do
  254.     if (Control is TTabSheet) and not Inserting then
  255.       AddImages;
  256.   inherited
  257. end;
  258.  
  259. procedure TColorPageControl.AlignControls(AControl: TControl;
  260.   var Rect: TRect);
  261. begin
  262.   inherited AlignControls(AControl, Rect);
  263.   if (AControl is TTabSheet) then
  264.     with TTabSheet(AControl) do
  265.     begin
  266.       //Here the TabSheet Tag is set to hold the image index
  267.       Tag := TabIndex;
  268.       ISetTabImage(Tag, Tag);
  269.       if Images <> nil then
  270.         Perform(tcm_SetImageList, 0, Longint(Images.Handle))
  271.     end;
  272. end;
  273.  
  274. procedure TColorPageControl.CreateParams(var Params: TCreateParams);
  275. const
  276.   ButtonStyle: array[Boolean] of LongInt = (0, tcs_Buttons);
  277.   OwnStyle: array[Boolean] of LongInt = (0, tcs_OwnerDrawFixed);
  278.   VerticalStyle: array[TTabVPosition] of LongInt =
  279.     (0, tcs_Vertical, tcs_Right or tcs_Vertical);
  280. begin
  281.   inherited;
  282.   with Params do
  283.   begin
  284.     if VerticalStyle[FTabVPosition] <> 0 then
  285.       Style := Style and not tcs_Bottom;
  286.     //When ScrollOpposite is set True, buttons don't get drawn
  287.     //Also, the control is unable to do buttons properly
  288.     //When tabs are at bottom or right
  289.     FTabButtons := FTabButtons and not
  290.       {$ifndef OldCommCtrl}
  291.       ScrollOpposite and not
  292.       {$endif}
  293.       ({$ifndef OldCommCtrl}(TabPosition = tpBottom) or {$endif}
  294.        (FTabVPosition = tvpRight));
  295.     Style := Style or ButtonStyle[FTabButtons]
  296.                    or OwnStyle[FStyle = pcsOwnerDraw]
  297.                    or VerticalStyle[FTabVPosition];
  298.    end;
  299. end;
  300.  
  301. procedure TColorPageControl.CreateWnd;
  302. begin
  303.   inherited;
  304.   AddImages;
  305.   //Force a realign and repositioning of tabsheets
  306.   //this is needed for the new vertical and horizontal styles
  307.   PostMessage(Handle, wm_Size, size_Restored,
  308.     MakeLong(Width, Height));
  309.   Realign
  310. end;
  311.  
  312. procedure TColorPageControl.Notification(AComponent: TComponent;
  313.   Operation: TOperation);
  314. begin
  315.   //Always make sure the linked imagelist's removal is tracked
  316.   inherited Notification(AComponent, Operation);
  317.   if (Operation = opRemove) and (AComponent = Images) then
  318.     Images := nil
  319. end;
  320.  
  321. procedure TColorPageControl.SetImages(Value: TImageList);
  322. begin
  323.   if FImages <> Value then
  324.   begin
  325.     FImages := Value;
  326.     //Just in case the Images component is on another form,
  327.     //we need to make sure Delphi tells us when it gets deleted
  328.     if FImages <> nil then
  329.       FImages.FreeNotification(Self);
  330.     AddImages;
  331.   end
  332. end;
  333.  
  334. procedure TColorPageControl.SetStyle(Value: TPageControlStyle);
  335. begin
  336.   if Value <> FStyle then
  337.   begin
  338.     FStyle := Value;
  339.     RecreateWnd;
  340.   end;
  341. end;
  342.  
  343. procedure TColorPageControl.SetTabButtons(Value: Boolean);
  344. begin
  345.   if Value <> FTabButtons then
  346.   begin
  347.     FTabButtons := Value;
  348.     //Can't have buttons at bottom or right of control
  349.     //The Windows control can't handle it...
  350.     if Value then
  351.     begin
  352.       if FTabVPosition = tvpRight then
  353.         FTabVPosition := tvpNone;
  354.       {$ifndef OldCommCtrl}
  355.       if TabPosition = tpBottom then
  356.         TabPosition := tpTop;
  357.       {$endif}
  358.     end;
  359.     RecreateWnd;
  360.   end;
  361. end;
  362.  
  363. procedure TColorPageControl.SetTabVPosition(Value: TTabVPosition);
  364. begin
  365.   if  Value <> FTabVPosition then
  366.   begin
  367.     //When tabs are left/right, they turn into multiline
  368.     //automatically so we'd better set the MultiLine property
  369.     FTabVPosition := Value;
  370.     if Value <> tvpNone then
  371.       MultiLine := True;
  372.     RecreateWnd;
  373.   end;
  374. end;
  375.  
  376. procedure TColorPageControl.CNDrawItem(var Msg: TWMDrawItem);
  377. var
  378.   State: TOwnerDrawState;
  379. begin
  380.   if not Assigned(FCanvas) then
  381.     FCanvas := TCanvas.Create;
  382.   with Msg.DrawItemStruct^ do
  383.   begin
  384.     //The low byte of ItemState is the bitmap that our set requires
  385.     State := TOwnerDrawState(WordRec(Word(ItemState)).Lo);
  386.     FCanvas.Handle := hDC;
  387.     FCanvas.Font   := Font;
  388.     FCanvas.Brush  := Brush;
  389.     if Integer(itemID) >= 0 then
  390.       DrawItem(itemID, rcItem, State);
  391.     FCanvas.Handle := 0;
  392.   end;
  393. end;
  394.  
  395. procedure TColorPageControl.DefaultDrawTab(Index: Integer;
  396.   ARect: TRect; State: TOwnerDrawState);
  397. var
  398.   S: String;
  399.   X, Y: Integer;
  400. begin
  401.   //Do a bit of default drawing when the
  402.   //component user is'nt doing it
  403.   FCanvas.FillRect(ARect);
  404.   S := Pages[Index].Caption;
  405.   X := (ARect.Right + ARect.Left - FCanvas.TextWidth(S)) div 2;
  406.   Y := (ARect.Bottom + ARect.Top + 4 - FCanvas.TextHeight(S)) div 2;
  407.   //Active tab has text _slightly_ higher
  408.   if odSelected in State then
  409.     Dec(Y, 3);
  410.   FCanvas.TextOut(X, Y, S);
  411. end;
  412.  
  413. procedure Register;
  414. begin
  415.   RegisterComponents('Clinic', [TColorPageControl]);
  416. end;
  417.  
  418. end.